package com.ziiwee.reduce.describe.types;

import com.ziiwee.reduce.describe.FieldDescribe;
import com.ziiwee.reduce.describe.TypeDescribe;
import org.springframework.core.ResolvableType;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ziiwee
 * @date 2020/8/12
 */
public class DefaultTypeProviderContext implements TypeProviderContext {

    protected final Map<String, TypeDescribe> cache = new HashMap<>();

    protected final TypeProviderRegister register;

    public DefaultTypeProviderContext() {
        register = new DefaultTypeProviderRegister();
    }

    public DefaultTypeProviderContext(TypeProviderRegister register) {
        this.register = register;
    }

    @Override
    public Map<String, TypeDescribe> getCache() {
        return cache;
    }

    @Override
    public void putCache(String key, TypeDescribe typed) {
        cache.put(key, typed);
    }

    @Override
    public TypeDescribe render(ResolvableType type) {
        if (type.resolve() == null) {
            return null;
        }
        TypeDescribe typed = cache.get(type.toString());
        if (typed != null) {
            return typed;
        }
        for (TypeProvider provider : register.getProviders()) {
            if (provider.support(type)) {
                typed = provider.render(type, this);
                toCache(type, typed);
                return typed;
            }
        }
        typed = renderNew(type);
        toCache(type, typed);
        return typed;
    }

    @Override
    public TypeDescribe renderNew(ResolvableType type) {
        if (type.resolve() == null) {
            return null;
        }
        return register.getDefaultTypeDescribeParser().parse(type);
    }

    @Override
    public FieldDescribe renderField(Field field) {
        FieldDescribe parse = register.getDefaultFieldDescribeParser().parse(field);
        if (cache.get(field.getType().toString()) == null) {
            render(parse.getType());
        }
        return parse;
    }

    private void toCache(ResolvableType type, TypeDescribe typed) {
        if (type == null || typed == null) {
            return;
        }
        cache.put(type.toString(), typed);
        for (FieldDescribe field : typed.getFields()) {
            if (cache.get(field.getType().toString()) == null) {
                render(field.getType());
            }
        }
    }
}
